/**
 * 深度合并
 */
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
    return hasOwnProperty.call(obj, key);
}
function makeMap(list) {
    const map = Object.create(null);
    for (let i = 0; i < list.length; i++) {
        map[list[i]] = true;
    }
    return (val) => !!map[val];
}
const objectToString = Object.prototype.toString;
function isPlainObject(val) {
    return objectToString.call(val) === '[object Object]';
}
function isPrimitive(val) {
    return (val === null ||
        typeof val === 'string' ||
        typeof val === 'number' ||
        typeof val === 'bigint' ||
        typeof val === 'symbol' ||
        typeof val === 'boolean' ||
        typeof val === 'undefined');
}
// 数组去重，不保证顺序
function unique(list) {
    const res = [];
    for (let i = 0, len = list.length; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            if (list[i] === list[j]) {
                j = ++i;
            }
        }
        res.push(list[i]);
    }
    return res;
}
function deepMerge(o, n, dp, ignores) {
    const leftRecord = new WeakMap();
    const rightRecord = new WeakMap();
    const valueRecord = new WeakMap();
    const ignoresMap = makeMap(ignores || []);
    const isArray = Array.isArray;
    const isAllRefs = (a, b) => {
        if (leftRecord.has(a) || rightRecord.has(a)) {
            return leftRecord.has(b) || rightRecord.has(b);
        }
        return false;
    };
    const clone = (v) => {
        if (isPrimitive(v) || typeof v === 'function') {
            return v;
        }
        else if (valueRecord.has(v)) {
            return valueRecord.get(v);
        }
        else if (leftRecord.has(v)) {
            return leftRecord.get(v);
        }
        else if (rightRecord.has(v)) {
            return rightRecord.get(v);
        }
        else if (isArray(v)) {
            if (dp)
                v = unique(v);
            const arr = [];
            valueRecord.set(v, arr);
            for (let i = 0, len = v.length; i < len; i++) {
                arr[i] = clone(v[i]);
            }
            return arr;
        }
        else if (typeof v === 'object') {
            const obj = {};
            valueRecord.set(v, obj);
            const keys = Reflect.ownKeys(v);
            keys.forEach((key) => (obj[key] = clone(v[key])));
            return obj;
        }
    };
    const setValue = (r, k, key) => {
        if (r.has(k)) {
            return r.get(k);
        }
        else {
            if (ignoresMap(key)) {
                return k;
            }
            const val = clone(k);
            if (!isPrimitive(val) && typeof val !== 'function') {
                r.set(k, val);
            }
            return val;
        }
    };
    const mergeObject = (l, r) => {
        const res = {};
        const leftKeys = Reflect.ownKeys(l);
        const rightKeys = Reflect.ownKeys(r);
        leftRecord.set(l, res);
        rightRecord.set(r, res);
        leftKeys.forEach((key) => {
            const lv = l[key];
            const rv = r[key];
            if (hasOwn(r, key)) {
                if (isArray(lv) && isArray(rv)) {
                    const item = clone([...lv, ...rv]);
                    res[key] = dp ? unique(item) : item;
                }
                else if (isPlainObject(lv) && isPlainObject(rv)) {
                    res[key] = isAllRefs(lv, rv)
                        ? leftRecord.get(lv)
                        : mergeObject(lv, rv);
                }
                else {
                    res[key] = setValue(rightRecord, rv, key);
                }
            }
            else {
                res[key] = setValue(leftRecord, lv, key);
            }
        });
        rightKeys.forEach((key) => {
            if (hasOwn(res, key))
                return;
            res[key] = setValue(rightRecord, r[key], key);
        });
        return res;
    };
    return mergeObject(o, n);
}

export { deepMerge as default };
